﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Xml.Linq;
using Expression = System.Linq.Expressions.Expression;

namespace GaleSong
{
    /// <summary>
    ///     Extension class for WPF.
    /// </summary>
    public static class WpfExtension
    {
        /// <summary>
        ///     Sets the value of a dependency property on <paramref name="targetObject" /> using a markup extension.
        /// </summary>
        /// <remarks>
        ///     This method does not support markup extensions like x:Static that depend on
        ///     having a XAML file as context.
        /// </remarks>
        public static void SetValueToExtension(this DependencyObject targetObject, DependencyProperty property,
            MarkupExtension markupExtension)
        {
            if(targetObject == null)
                throw new ArgumentNullException("targetObject");
            if(property == null)
                throw new ArgumentNullException("property");
            if(markupExtension == null)
                throw new ArgumentNullException("markupExtension");

            var serviceProvider = new SetValueToExtensionServiceProvider(targetObject, property);
            targetObject.SetValue(property, markupExtension.ProvideValue(serviceProvider));
        }

        public static T FindVisualChild<T>(this DependencyObject depObj, string childName)
            where T : DependencyObject
        {
            // Confirm obj is valid. 
            if(depObj == null) return null;

            // success case
            if(depObj is T && ((FrameworkElement)depObj).Name == childName)
                return depObj as T;

            var frameworkContentElement = depObj as FrameworkContentElement;
            if(frameworkContentElement != null)
            {
                IEnumerator enumrator = LogicalTreeHelper.GetChildren(frameworkContentElement).GetEnumerator();
                if(enumrator.Current != null)
                {
                    var obj = FindVisualChild<T>(enumrator.Current as DependencyObject, childName);
                    if(obj != null)
                    {
                        return obj;
                    }
                }
            }
            else
            {
                for(int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);

                    var obj = FindVisualChild<T>(child, childName);

                    if(obj != null)
                        return obj;
                }
            }

            return null;
        }

        public static T FindVisualChild<T>(this DependencyObject depObj)
            where T : DependencyObject
        {
            // Confirm obj is valid. 
            if(depObj == null) return null;

            // success case
            if(depObj is T)
                return depObj as T;

            for(int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);

                var obj = FindVisualChild<T>(child);

                if(obj != null)
                    return obj;
            }

            return null;
        }

        public static void Merge(this Style style, Style otherStyle)
        {
            if(style == null)
            {
                throw new ArgumentNullException("style");
            }
            if(otherStyle == null)
            {
                throw new ArgumentNullException("otherStyle");
            }

            if(style.TargetType.IsAssignableFrom(otherStyle.TargetType))
            {
                style.TargetType = otherStyle.TargetType;
            }

            foreach(SetterBase currentSetter in otherStyle.Setters)
            {
                style.Setters.Add(currentSetter);
            }

            foreach(TriggerBase currentTrigger in otherStyle.Triggers)
            {
                style.Triggers.Add(currentTrigger);
            }

            // This code is only needed when using DynamicResources.
            foreach(object key in otherStyle.Resources.Keys)
            {
                style.Resources[key] = otherStyle.Resources[key];
            }
        }

        private sealed class SetValueToExtensionServiceProvider : IServiceProvider, IProvideValueTarget
        {
            private readonly DependencyObject _targetObject;
            private readonly DependencyProperty _targetProperty;

            public SetValueToExtensionServiceProvider(DependencyObject targetObject, DependencyProperty property)
            {
                this._targetObject = targetObject;
                _targetProperty = property;
            }

            public object TargetObject
            {
                get { return _targetObject; }
            }

            public object TargetProperty
            {
                get { return _targetProperty; }
            }

            public object GetService(Type serviceType)
            {
                if(serviceType == typeof(IProvideValueTarget))
                    return this;
                return null;
            }
        }
    }

    /// <summary>
    ///     ResourceManager extensions.
    /// </summary>
    public static class ResourceManagerExtension
    {
        /// <summary>
        ///     Get string from resource, return string.Empty if failed.
        /// </summary>
        /// <param name="manager">The manager.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static string TryGetString(this ResourceManager manager, string name)
        {
            try
            {
                return manager.GetString(name);
            }
            catch
            {
                return string.Empty;
            }
        }

        /// <summary>
        ///     Get object from resource, return null if failed.
        /// </summary>
        /// <param name="manager">The manager.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static object TryGetObject(this ResourceManager manager, string name)
        {
            try
            {
                return manager.GetObject(name);
            }
            catch
            {
                return null;
            }
        }

        public static ImageSource GetImageSource(this ResourceManager manager, string name)
        {
            object bmp = null;

            //1. Get Image from resource manager
            if(manager != null)
            {
                bmp = manager.TryGetObject(name);
            }

            //2. Get Image from embedded resource(name = uri)
            if(bmp == null)
            {
                try
                {
                    bmp = new Bitmap(Application.GetResourceStream(new Uri(name, UriKind.RelativeOrAbsolute)).Stream);
                }
                catch
                {
                    return null;
                }
            }

            IntPtr hBitmap = ((Bitmap)bmp).GetHbitmap();
            BitmapSource bs;
            try
            {
                bs = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero,
                    Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                bs.Freeze();
            }
            finally
            {
                NativeMethods.DeleteObject(hBitmap);
            }
            return bs;
        }
    }

    public static class ImageSourceExtension
    {
        public static Bitmap ToBitmap(this BitmapSource bitmapsource)
        {
            using(var stream = new MemoryStream())
            {
                BitmapEncoder enc = new BmpBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(bitmapsource));
                enc.Save(stream);

                using(var tempBitmap = new Bitmap(stream))
                {
                    return new Bitmap(tempBitmap);
                }
            }
        }

        public static BitmapImage GetBitmapSource(this System.Drawing.Bitmap bmp)
        {
            BitmapImage bitmapImage = new BitmapImage();
            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
            {
                bmp.Save(ms, ImageFormat.Png);
                bitmapImage.BeginInit();
                bitmapImage.StreamSource = ms;
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.EndInit();
                bitmapImage.Freeze();
            }
            return bitmapImage;
        }

        public static BitmapSource ToBitmapSource(this System.Drawing.Bitmap source)
        {
            BitmapSource bitSrc = null;

            var hBitmap = source.GetHbitmap();

            try
            {
                bitSrc = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                    hBitmap,
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }
            catch (Win32Exception)
            {
                bitSrc = null;
            }
            finally
            {
                NativeMethods.DeleteObject(hBitmap);
            }

            return bitSrc;
        }

    }


    /// <summary>
    ///     Extension class for params.
    /// </summary>
    public static class ParamsExtensions
    {
        /// <summary>
        ///     Checks whether the object is equal to one of the specified values.
        /// </summary>
        public static bool In<T>(this T value, params T[] values)
        {
            return values.Contains(value);
        }
    }

    /// <summary>
    ///     Extension class for <see cref="IList" />.
    /// </summary>
    public static class ListExtension
    {
        /// <summary>
        ///     Foreach extension for IList;
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list">The collection.</param>
        /// <param name="action">The action.</param>
        public static void ForEach<T>(this IList list, Action<T> action)
        {
            foreach(T obj in list)
            {
                action(obj);
            }
        }

        /// <summary>
        ///     Foreach extension with predicate filter for IList;
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list">The collection.</param>
        /// <param name="action">The action.</param>
        /// <param name="filter">The filter.</param>
        public static void ForSpecification<T>(this IList list, Action<T> action, Predicate<T> filter)
        {
            foreach(T obj in list)
            {
                if(filter(obj))
                    action(obj);
            }
        }
    }

    /// <summary>
    ///     Extension class for <see cref="IEnumerable" />.
    /// </summary>
    public static class EnumerableExtension
    {
        /// <summary>
        ///     Foreach extension for IEnumerable;
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection">The collection.</param>
        /// <param name="action">The action.</param>
        public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
        {
            foreach(T obj in collection)
            {
                action(obj);
            }
        }

        /// <summary>
        ///     Foreach extension with predicate filter for IEnumerable;
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection">The collection.</param>
        /// <param name="action">The action.</param>
        /// <param name="filter">The filter.</param>
        public static void ForSpecification<T>(this IEnumerable<T> collection, Action<T> action, Predicate<T> filter)
        {
            foreach(T obj in collection)
            {
                if(filter(obj))
                    action(obj);
            }
        }

        /// <summary>
        ///     Indexes the of.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection">The collection.</param>
        /// <param name="item">The item.</param>
        public static int IndexOf<T>(this IEnumerable<T> collection, T item)
        {
            int index = 0;
            foreach(T obj in collection)
            {
                if(Equals(obj, item))
                {
                    return index;
                }
                index++;
            }
            return -1;
        }
    }

    /// <summary>
    ///     Extension class for <see cref="MemberInfo" />.
    /// </summary>
    public static class MemberInfoExtension
    {
        /// <summary>
        ///     Determines whether the specified member has attribute.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="member">The member.</param>
        /// <returns>
        ///     <c>true</c> if the specified member has attribute; otherwise, <c>false</c>.
        /// </returns>
        public static bool HasAttribute<T>(this MemberInfo member) where T : Attribute
        {
            return member.GetCustomAttributes(typeof(T), false).Any();
        }
    }

    /// <summary>
    ///     Extension class for <see cref="IServiceContainer" />.
    /// </summary>
    public static class ServiceContainerExtension
    {
        /// <summary>
        ///     Gets the specific service.
        /// </summary>
        /// <typeparam name="T">service type</typeparam>
        /// <param name="container">The container.</param>
        /// <returns>&gt;service</returns>
        public static T GetService<T>(this IServiceContainer container) where T : class
        {
            var t = container.GetService(typeof(T)) as T;
            if(t == null)
            {
                throw new Exception("Can not find the Type : " + typeof(T));
            }
            return t;
        }
    }

    /// <summary>
    ///     Extension class for int array/>.
    /// </summary>
    public static class IntArrayExtension
    {
        /// <summary>
        ///     Gets the specific service.
        /// </summary>
        /// <returns>service</returns>
        public static int[] DeepClone(this int[] array)
        {
            if(array == null)
            {
                return null;
            }

            var target = new int[array.Length];
            Array.Copy(array, target, array.Length);
            return target;
        }
    }

    public static class AssemblyExtension
    {
        public static bool ContainsAtrribute<T>(this Assembly assembly) where T : Attribute
        {
            return assembly.GetCustomAttributes(typeof(T), false).Length > 0;
        }

        public static T GetAtrribute<T>(this Assembly assembly) where T : Attribute
        {
            object[] attributes = assembly.GetCustomAttributes(typeof(DesignUICultureAttribute), false);
            if(attributes.Length > 0)
            {
                return attributes[0] as T;
            }
            return null;
        }
    }

    public static class TypeExtension
    {
        /// <summary>
        ///     Gets the interface explicit property.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="interfaceName">Name of the interface.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns></returns>
        public static PropertyInfo GetInterfaceProperty(this Type type, string interfaceName, string propertyName)
        {
            Type typeInterface;
            if(interfaceName != null && interfaceName.Contains('<'))
            {
                string[] names = interfaceName.Trim().Split('<', '>');
                typeInterface = type.GetInterfaces().FirstOrDefault(i => names.All(j => i.FullName.Contains(j)));
            }
            else
            {
                typeInterface = type.GetInterfaces().FirstOrDefault(i => i.Name == interfaceName);
            }

            if(typeInterface != null)
            {
                return typeInterface.GetProperty(propertyName);
            }
            return null;
        }

        /// <summary>
        ///     Determines whether it is the simple type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>
        ///     <c>true</c> if [is simple type] [the specified type]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsSimpleType(this Type type)
        {
            return (type.IsPrimitive ||
                    type.IsEnum ||
                    type == typeof(string));
        }

        /// <summary>
        ///     Determines whether the specific type is this type or subclass.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="c">The c.</param>
        /// <returns>
        ///     <c>true</c> if it is; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsThisOrSubclassOf(this Type type, Type c)
        {
            return type == c || type.IsSubclassOf(c);
        }

        /// <summary>
        ///     Determines whether the specific type is this type or baseClass or interface assign from.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="subType">The subType.</param>
        /// <returns>
        ///     <c>true</c> if it is; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsConvertableFrom(this Type type, Type subType)
        {
            if (type == null || subType == null)
            {
                return false;
            }
            return type == subType || subType.IsSubclassOf(type) || type.IsAssignableFrom(subType);
        }

        /// <summary>
        ///     Defaults the value.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        public static object DefaultValue(this Type type)
        {
            if(type == null)
            {
                return new ArgumentNullException("type");
            }
            return type.IsValueType ? Activator.CreateInstance(type) : null;
        }
    }

    public static class XDocumentExtension
    {
        /// <summary>
        ///     Toes the string with declaration.
        /// </summary>
        /// <param name="doc">The doc.</param>
        /// <returns></returns>
        public static string ToStringWithDeclaration(this XDocument doc)
        {
            var builder = new StringBuilder();
            using(TextWriter writer = new StringWriter(builder))
            {
                doc.Save(writer);
            }
            return builder.ToString();
        }
    }

    public static class ExpressionExtension
    {
        public const string IndexMark = "[";

        public static string GetFullName(this Expression expression)
        {
            if (expression == null)
            {
                return null;
            }

            return GetFullName(expression, null).FirstRight(".");
        }

        public static string GetName(this Expression expression)
        {
            if (expression == null)
            {
                return null;
            }

            var lambda = (LambdaExpression)expression;
            MemberExpression memberExpression;
            if(lambda.Body is UnaryExpression)
            {
                var unaryExpression = (UnaryExpression)lambda.Body;
                memberExpression = (MemberExpression)unaryExpression.Operand;
            }
            else
            {
                memberExpression = (MemberExpression)lambda.Body;
            }
            return memberExpression.Member.Name;
        }

        public static string GetFullName(Expression expression, string formatter)
        {
            if (expression == null)
            {
                return null;
            }

            string result;

            if (expression is LambdaExpression)
            {
                result = GetFullName(((LambdaExpression)expression).Body, null);
            }
            else if (expression is UnaryExpression)
            {
                result = GetFullName(((UnaryExpression)expression).Operand, null);
            }
            else if (expression is MethodCallExpression)
            {
                var methodCallExpression = (MethodCallExpression)expression;
                if (methodCallExpression.Arguments.Count == 1) //Indexer
                {
                    result = string.Format("{0}[{1}]", GetFullName(methodCallExpression.Object, null), GetFullName(methodCallExpression.Arguments[0], null));
                }
                else
                {
                    result = string.Format("{0}.{1}", GetFullName(methodCallExpression.Object, null), GetFullName(methodCallExpression.Arguments.Last(), null));
                }
            }
            else if (expression is ConstantExpression)
            {
                var value = ((ConstantExpression)expression).Value;
                if (value is MethodInfo)
                {
                    result = ((MethodInfo)value).Name;
                }
                else
                {
                    result = value.ToString();
                }
            }
            else if (expression is MemberExpression)
            {
                var memberExpression = (MemberExpression)expression;
                if (memberExpression.ToString().StartsWith("value(")) //Need to compile
                {
                    result = Expression.Lambda(memberExpression).Compile().DynamicInvoke().ToString();
                }
                else
                {
                    result = GetFullName(((MemberExpression)expression).Expression, "{0}." + ((MemberExpression)expression).Member.Name);
                }
            }
            else
            {
                result = expression.ToString();
            }

            if (formatter != null)
            {
                return string.Format(formatter, result);
            }
            return result;
        }
    }

    public static class StringExtension
    {
        /// <summary>
        ///     Rights the specified string.
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="content">The content.</param>
        /// <returns></returns>
        public static string FirstRight(this string str, string content)
        {
            if(!str.Contains(content))
            {
                return string.Empty;
            }
            int index = str.IndexOf(content, StringComparison.Ordinal);
            return index < str.Length - 1
                ? str.Substring(index + content.Length, str.Length - index - content.Length)
                : string.Empty;
        }

        /// <summary>
        ///     Rights the specified string.
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="content">The content.</param>
        /// <returns></returns>
        public static string Right(this string str, string content)
        {
            if(!str.Contains(content))
            {
                return string.Empty;
            }
            int index = str.LastIndexOf(content, StringComparison.Ordinal);
            return index < str.Length - 1
                ? str.Substring(index + content.Length, str.Length - index - content.Length)
                : string.Empty;
        }

        /// <summary>
        ///     Rights the specified string.
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="content">The content.</param>
        /// <returns></returns>
        public static string Left(this string str, string content)
        {
            if(!str.Contains(content))
            {
                return string.Empty;
            }
            int index = str.IndexOf(content, StringComparison.Ordinal);
            return str.Substring(0, index);
        }

        /// <summary>
        ///     Rights the specified string.
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="content">The content.</param>
        /// <returns></returns>
        public static string LastLeft(this string str, string content)
        {
            if(!str.Contains(content))
            {
                return string.Empty;
            }
            int index = str.LastIndexOf(content, StringComparison.Ordinal);
            return str.Substring(0, index);
        }

        /// <summary>
        ///     Ignores the case equals.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <param name="target">The target.</param>
        /// <returns></returns>
        public static bool IgnoreCaseEquals(this string str, string target)
        {
            return String.Compare(str, target, StringComparison.OrdinalIgnoreCase) == 0;
        }

        /// <summary>
        ///     Encrypts the string to encrypt.
        /// </summary>
        /// <param name="toEncrypt">To encrypt.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static string Encrypt(this string toEncrypt, string key)
        {
            byte[] toEncryptArray = Encoding.UTF8.GetBytes(toEncrypt);

            var hashmd5 = new MD5CryptoServiceProvider();
            byte[] keyArray = hashmd5.ComputeHash(Encoding.UTF8.GetBytes(key));

            hashmd5.Clear();

            var tdes = new TripleDESCryptoServiceProvider
            {
                Key = keyArray,
                Mode = CipherMode.ECB,
                Padding = PaddingMode.PKCS7
            };

            ICryptoTransform cTransform = tdes.CreateEncryptor();
            byte[] resultArray =
                cTransform.TransformFinalBlock(toEncryptArray, 0,
                    toEncryptArray.Length);
            tdes.Clear();

            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }

        /// <summary>
        ///     Decrypts the specified cipher string.
        /// </summary>
        /// <param name="cipherString">The cipher string.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static string Decrypt(this string cipherString, string key)
        {
            byte[] toEncryptArray = Convert.FromBase64String(cipherString);

            var hashmd5 = new MD5CryptoServiceProvider();
            byte[] keyArray = hashmd5.ComputeHash(Encoding.UTF8.GetBytes(key));
            hashmd5.Clear();

            var tdes = new TripleDESCryptoServiceProvider
            {
                Key = keyArray,
                Mode = CipherMode.ECB,
                Padding = PaddingMode.PKCS7
            };

            ICryptoTransform cTransform = tdes.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(
                toEncryptArray, 0, toEncryptArray.Length);
            tdes.Clear();

            return Encoding.UTF8.GetString(resultArray);
        }
    }

    /// <summary>
    ///     string extension for split command line to string[]
    /// </summary>
    public static class CmdLineExtension
    {
        public static string[] SplitArgs(this string cmd)
        {
            int count;
            IntPtr ret = CommandLineToArgvW(cmd, out count);

            if(ret == IntPtr.Zero)
                throw new ArgumentException("Unable to split argument.");
            try
            {
                var results = new string[count];

                for(int i = 0; i < count; i++)
                    results[i] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(ret, i * IntPtr.Size));
                return results;
            }
            finally
            {
                LocalFree(ret);
            }
        }

        [DllImport("shell32.dll", SetLastError = true)]
        private static extern IntPtr CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmd, out int count);

        [DllImport("kernel32.dll")]
        private static extern IntPtr LocalFree(IntPtr mem);
    }

    /// <summary>
    ///     Extension for XElement.
    /// </summary>
    public static class XElementExtension
    {
        public static Dictionary<Type, ICustomConverter> Converters = new Dictionary<Type, ICustomConverter>();

        /// <summary>
        ///     Opens the or create element.
        /// </summary>
        /// <param name="root">The root.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static XElement OpenOrCreateElement(this XElement root, string name)
        {
            XElement elemnt = root.Element(name) ?? CreateElement(root, name);
            return elemnt;
        }

        /// <summary>
        ///     Opens the or create element.
        /// </summary>
        /// <param name="root">The root.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static XElement CreateElement(this XElement root, string name)
        {
            var elemnt = new XElement(name);
            root.Add(elemnt);
            return elemnt;
        }

        public static void SetAttributeValueEx(this XElement root, string attributeName, object value,
            bool ignoreNull = true)
        {
            if(value == null)
            {
                if(!ignoreNull)
                {
                    root.SetAttributeValue(attributeName, string.Empty);
                }
                return;
            }

            if(value is string)
            {
                root.SetAttributeValue(attributeName, value);
            }
            else
            {
                Type type = value.GetType();
                if(Converters.ContainsKey(type))
                {
                    root.SetAttributeValue(attributeName, Converters[type].ConvertBack(value));
                }
                else
                {
                    root.SetAttributeValue(attributeName,
                        TypeDescriptor.GetConverter(type).ConvertToString(value));
                }
            }
        }

        public static string GetAttributeValue(this XElement root, string attributeName)
        {
            XAttribute attribute = root.Attribute(attributeName);
            if(attribute != null)
            {
                return attribute.Value;
            }
            return null;
        }

        public static T GetAttributeValue<T>(this XElement root, string attributeName, T defaultValue = default(T))
        {
            XAttribute attribute = root.Attribute(attributeName);
            if(attribute != null)
            {
                string value = attribute.Value;
                Type type = typeof(T);
                if(Converters.ContainsKey(type))
                {
                    return (T)Converters[type].Convert(value);
                }
                return (T)TypeDescriptor.GetConverter(type).ConvertFromString(null, CultureInfo.InvariantCulture, value);
            }
            return defaultValue;
        }

        public static string GetElementValue(this XElement root, string elementName)
        {
            XElement element = root.Element(elementName);
            if(element != null)
            {
                return element.Value;
            }
            return null;
        }

        public static T GetElementValue<T>(this XElement root, string elementName, T defaultValue = default(T))
        {
            XElement element = root.Element(elementName);
            if(element != null)
            {
                string value = element.Value;
                Type type = typeof(T);
                if(Converters.ContainsKey(type))
                {
                    return (T)Converters[type].Convert(value);
                }
                return (T)TypeDescriptor.GetConverter(type).ConvertFromString(null, CultureInfo.InvariantCulture, value);
            }
            return defaultValue;
        }

        public static void SetElementValueEx(this XElement root, string elementName, object value,
            bool ignoreNull = true)
        {
            if(value == null)
            {
                if(!ignoreNull)
                {
                    root.Add(new XElement(elementName));
                }
                return;
            }

            if(value is string)
            {
                root.Add(new XElement(elementName, value));
            }
            else
            {
                Type type = value.GetType();
                if(Converters.ContainsKey(type))
                {
                    root.Add(new XElement(elementName, Converters[type].ConvertBack(value)));
                }
                else
                {
                    root.Add(new XElement(elementName, TypeDescriptor.GetConverter(type).ConvertToString(value)));
                }
            }
        }
    }

    public static class TimeSpanExtensions
    {
        public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> selector)
        {
            return source.Select(selector).Aggregate(TimeSpan.Zero, (t1, t2) => t1 + t2);
        }
    }

    public static class FloatExtensions
    {
        public static bool AlmostEquals(this float float1, float float2, float precision = 0.001f)
        {
            return (Math.Abs(float1 - float2) <= precision);
        }
    }

    public static class DoubleExtensions
    {
        public static bool AlmostEquals(this double double1, double double2, double precision = 0.001)
        {
            return (Math.Abs(double1 - double2) <= precision);
        }
    }

    public static class IListExtensions
    {
        public static bool ListEquals(this IList list1, IList list2)
        {
            if(list1 == null && list2 == null)
            {
                return true;
            }

            if(list1 == null || list2 == null)
            {
                return false;
            }

            if(list1.Count != list2.Count)
            {
                return false;
            }

            for(int i = 0; i < list1.Count; i++)
            {
                if(!list1[i].Equals(list2[i]))
                {
                    return false;
                }
            }
            return true;
        }
    }

    public interface ICustomConverter
    {
        object Convert(string value);
        string ConvertBack(object obj);
    }
}